
package com.studiofortress.sf.graphics;

import java.util.LinkedList;
import javax.media.opengl.GL;

/**
 * This is a thin abstract class to help wrapping OpenGL pipeline calls down
 * into an object. This will try to take advantage of the underlying display
 * lists (but this is not guaranteed). For this reason it's important that the
 * paint method is stateless and renders the exact same scene on every call.
 *
 * This will automatically uninitialize the display list in the underlying
 * OpenGL pipeline when this object is collected.
 *
 * @author Joseph Lenton
 */
public abstract class GLDisplayList
{
    private static final LinkedList<Integer> UNINITIALIZE_QUEUE =
            new LinkedList<Integer>();

    /**
     * Uninitializes all the queued GLDisplayLists that are ready to be
     * uninitialized from the given OpenGL pipeline.
     * This must be the same pipeline that this was
     * @param gl The GL pipeline to remove the display list from.
     */
    static void uninitializeDisplayLists(GL gl)
    {
        while (!UNINITIALIZE_QUEUE.isEmpty()) {
            gl.glDeleteLists(UNINITIALIZE_QUEUE.remove(), 1);
        }
    }

    private int displayList;
    private boolean hasDisplayList;
    private GL gl;

    /**
     * Trivial constructor.
     */
    public GLDisplayList()
    {
        hasDisplayList = false;
        this.gl = null;
    }

    /**
     * Adds this GLDisplayLists underlying display list integer to be queued to
     * be removed from the underlying OpenGL pipeline.
     * @throws java.lang.Throwable
     */
    @Override
    protected void finalize()
            throws Throwable
    {
        super.finalize();
        UNINITIALIZE_QUEUE.add(displayList);
    }

    /**
     * This is where you can define your OpenGL commands to be run. If this is
     * compiled using the OpenGL Display Lists then you can presume the code to
     * start and end compilation is called automatically.
     * You can also presume that the gl pipeline is not currently rendering when
     * it is passed in.
     *
     * @param gl The OpenGL pipeline for this to render it's contents too.
     */
    public abstract void paint(GL gl);

    /**
     * @return True if this has been compiled down to a display list, otherwise false.
     */
    final boolean hasDisplaylist()
    {
        return hasDisplayList;
    }

    /**
     * @return The OpenGL display list number that this GLDisplayList uses in the OpenGL pipeline.
     */
    final int getDisplayList()
    {
        return displayList;
    }

    /**
     * Records this GLDisplayLists paint method in the given OpenGL pipeline.
     * @param gl
     */
    void record(GL gl)
    {
        displayList = gl.glGenLists(1);
        this.gl = gl;

        gl.glNewList(displayList, GL.GL_COMPILE);
        paint(gl);
        gl.glEndList();
        
        hasDisplayList = true;
    }

    /**
     * Sets the new display list number to store for this GLDisplayList. This
     * object will now also presume it has one stored and so hasDisplayList()
     * will now return true.
     * @deprecated
     * @param displayList The underlying integer that represents this displayList in the OpenGL pipeline.
     */
    final void setDisplayList(final int displayList)
    {
        this.displayList = displayList;
        hasDisplayList = true;
    }
}
